package LFSR(
        LFSR(..), mkPolyLFSR, mkFeedLFSR,
        mkLFSR_4, mkLFSR_8, mkLFSR_16, mkLFSR_32, mkRCounter
        ) where

import List
import Vector

--@ \subsubsection{LFSR}
--@
--@ \index{LFSR@\te{LFSR} (package)|textbf}

--@ The \te{LFSR} package implements Linear Feedback Shift Registers (LFSRs).
--@ LFSRs can be used to obtain pseudorandom sequences, though their linearity
--@ permits easy cryptanalysis.\footnote{see \te{http://en.wikipedia.org/wiki/Linear\US{}feedback\US{}shift\US{}register} for details}
--@ The interface allows the value in the shifter register to be
--@ set (with \te{seed}), read (with \te{value}), and shifted (with \te{next}).
--@ When the value is shifted the least significant bit will be fed back
--@ according to the polynomial used when the LFSR was created.
--@ When a LFSR is created the start value is 1.
--@ \begin{libverbatim}
--@ interface LFSR #(type a);
--@     method Action seed(a x1);
--@     method a value();
--@     method Action next();
--@ endinterface: LFSR
--@ \end{libverbatim}
interface LFSR a =
    seed  :: a -> Action
    value :: a
    next  :: Action

--@ The \te{mkPolyLFSR} function creates a LFSR given a polynomial specified
--@ by the exponents that have a non-zero coefficient.  For example
--@ the polynominal $x^7+x^3+x^2+x$ is used by the expression
--@ \qbs{mkPolyLFSR (Cons(7, Cons(3, Cons(2, Cons(1, Nil)))))}.
--@ \begin{libverbatim}
--@ module mkPolyLFSR#(List#(Integer) taps) (LFSR#(Bit#(n)));
--@ \end{libverbatim}
mkPolyLFSR :: (IsModule m c) => List Integer -> m (LFSR (Bit n))
mkPolyLFSR taps = mkFeedLFSR (pack (map (\ n -> List.elem n taps) genList))

--@ The \te{mkFeedLFSR} function creates a LFSR where the polynomial is specified
--@ by the mask used for feedback.  If \qbs{r} is the state of the LFSR the
--@ next state is \qbs{if r[0] == 1} \qbs{then (r >\mbox{}> 1) \HAT~feed} \qbs{else r >\mbox{}> 1},
--@ where \qbs{feed} is the argument to \te{mkFeedLFSR}.
--@ \begin{libverbatim}
--@ module mkFeedLFSR#( Bit#(n) feed )( LFSR#(Bit#(n)) );
--@ \end{libverbatim}
mkFeedLFSR :: (IsModule m c) => Bit n -> m (LFSR (Bit n))
mkFeedLFSR feed = liftModule $
    module
      r :: Reg (Bit n) <- mkReg 1
      interface
        seed x = r := x
        value = r
        next = r := if {-(r&1) == 1-} r[0:0] == 1 then (r >> 1) ^ feed else r >> 1

--@ Some maximal length LFSRs.
--@ Many more can be found at \te{http://www-2.cs.cmu.edu/ {\TILDE}koopman/lfsr/}
--@ \begin{libverbatim}
--@ module mkLFSR_4 (LFSR#(Bit#(4)));
--@ mkLFSR_4   =  mkFeedLFSR( 4'h9 );
--@
--@ module mkLFSR_8 (LFSR#(Bit#(8)));
--@ mkLFSR_8   =  mkFeedLFSR( 8'h8E );
--@
--@ module mkLFSR_16 (LFSR#(Bit#(16)));
--@ mkLFSR_16  =  mkFeedLFSR( 16'h8016 );
--@
--@ module mkLFSR_32 (LFSR#(Bit#(32)));
--@ mkLFSR_32  =  mkFeedLFSR( 32'h80000057 );
--@ \end{libverbatim}

mkLFSR_4 :: (IsModule m c) => m (LFSR (Bit 4))
mkLFSR_4 = mkFeedLFSR 0x9
mkLFSR_8 :: (IsModule m c) => m (LFSR (Bit 8))
mkLFSR_8 = mkFeedLFSR 0x8e
mkLFSR_16 :: (IsModule m c) => m (LFSR (Bit 16))
mkLFSR_16 = mkFeedLFSR 0x8016
mkLFSR_32 :: (IsModule m c) => m (LFSR (Bit 32))
mkLFSR_32 = mkFeedLFSR 0x80000057

--@ The \te{mkRCounter} function creates a counter with a LFSR interface.
--@ This is useful during debugging when a non-random sequence is desired.
--@ This function can be used in place of the other mkLFSR module
--@ constructors, without changing any method calls or behavior.
--@ \begin{libverbatim}
--@ module mkRCounter#( Bit#(n) seed ) ( LFSR#(Bit#(n)) );
--@ \end{libverbatim}
mkRCounter :: (IsModule m c) => Bit n -> m (LFSR (Bit n))
mkRCounter seed = module
                   _cntr :: Reg (Bit n) <- mkReg seed
                   interface
                    seed v = _cntr._write v
                    value  = _cntr._read
                    next   = _cntr._write ( _cntr._read + 1 )

{-
{-# verilog testLFSR #-}
testLFSR :: Module Empty
testLFSR =
    module
        t1 :: ActionSeq <- aTest mkLFSR_4
        t2 :: ActionSeq <- aTest mkLFSR_8
        t3 :: ActionSeq <- aTest mkLFSR_16
--        t4 :: ActionSeq <- aTest mkLFSR_32
        t :: ActionSeq <- seqOfActionSeq $ t1 :> t2 :> t3:> nil
        o :: Once <- mkOnce t.start
    interface
        { }
    rules
        when True
         ==> o.start

aTest :: (Eq a, Bits a sa) => Module (LFSR a) -> Module ActionSeq
aTest lfsr =
    module
        l :: LFSR a <- lfsr
        x :: Reg a <- mkRegU
        n :: Reg (Bit 32) <- mkRegU
        let act a = actionSeq (a :> nil)
        init :: ActionSeq <- act { n := 0; x := l.value; l.next }
        loop :: ActionSeq <-
                forever $ \ break -> {
                        l.next;
                        n := n+1;
                        if l.value == x then break else {};
                        } :> nil
        fini :: ActionSeq <- act { $display "period %0d" n; }
        as :: ActionSeq <- seqOfActionSeq $
                init :>
                loop :>
                fini :>
                nil
    interface
        as
-}
